iT邦幫忙

2023 iThome 鐵人賽

DAY 12
0

何謂 Secret?

我們時常會需要處理一些機敏的資料,例如 API Token、資料庫帳號密碼等資訊。在 Pulumi 中,如果我們將這些資訊直接放在 Stack 檔案中,就有可能會洩漏這些敏感資訊。但如果要在執行的時候再由可信任的人去填入這些資料,又會讓自動化的流程不順暢。

這時我們就可以將機敏資訊放置於 Pulumi Secret 中。Pulumi 內建的 Secret 管理機制是由 Pulumi Cloud 代管 Secret key。如果不想要使用 Pulumi Cloud 代管 Secret Key,還可以選擇不同的 Secret Provider。

目前 Pulumi 支援的 Secret Provider 如下:

  • default:使用 Pulumi Cloud 管理 Key
  • passphrase:在 Local 使用一個密碼保護 Key
  • awskms:使用 AWS KMS 管理 key
  • azurekeyvault:使用 Azure Key Vault 管理 key
  • gcpkms:使用 GCP KMS 管理 key
  • hashivault:使用 Hashicorp Vault 管理 key

如果想要變更 Secret Provider,可以透過 pulumi stack change-secrets-provider 指令變更,使用方式請參考文件

Note:
change-secrets-provider 指令有一個特殊的用法,就是用來更改 passphrase 的密碼。如果目前的 Secret Provider 已經是 passphrase 了,但想要更改密碼,可以直接執行 pulumi stack change-secrets-provider passphrase 指令。輸入舊密碼後,再輸入兩次新密碼即可更改 passphrase 的密碼了。

透過 CLI 設定 secret config

上一篇文章介紹的如果使用 Config、設定 Config。那如果我們要設定的值是機敏資訊要怎麼做呢?

可以在設定 config 時透過 --secret 參數來達成。

例如以下指令就會設定 dbPassword config,內容為 test123,並會將值加密:

pulumi config set dbPassword test123 --secret

在 Stack 檔案中就能看到,dbPassword 的內容是被加密過的。

config:
  aws-vpc-ts:dbPassword:
    secure: AAABAP+SKJGIpeiPsE6RJSifIGkm2SCMBVODV0mq5jEytPQcULUu
  aws:region: ap-east-1

昨天有提到設定複雜的 config 可以透過直接修改 Stack 檔案達成。但如果複雜的 config 內容是 secret 的話,就還是得透過 CLI 指令來設置。

例如:pulumi config set --path "database.password" test123 --secret

透過 CLI 取得 secret

預設狀況下,透過 pulumi config 列出來的 config 列表中,會將 secret 標註為 [secret]。如果希望可以列出所有資料時,也將 secret 解密顯示的話,可以使用 --show-secrets 參數達成。

例如:

$ pulumi config --show-secrets
KEY          VALUE
aws:region   ap-east-1
dbPassword   test123

在程式中讀取 Secret

接著介紹如何在程式中讀取 Secret 的資料。讀取 Secret 與讀取 Config 的方式大同小異,都是透過 Config 類別來讀取,不同的地方有兩個:

  1. 呼叫的方法不同,Secre 需要被解密
  2. 回傳的型別不同,由於解密是一件非同步動作 (需要存取 Secret Provider 的 Key),因此回傳值一律是 Output 型別。

存取 Secret 的方法有:

  • getSecret
  • getSecretBoolean
  • getSecretNumber
  • getSecretObject
  • requireSecret
  • requireSecretBoolean
  • requireSecretNumber
  • requireSecretObject

這些方法其實與讀取 config 的方法相同,差別在於全部都多了 Secret 而已。如果忘記操作方式,可以參考前一篇文章

讀取 dbPassword

以下程式碼即可從 Stack 檔案中將 dbPassword 讀取出來。

const config = new pulumi.Config();
const secret: pulumi.Output<string> | undefined = config.getSecret('dbPassword');

這邊要特別注意的是,程式執行期間,讀取出來的 Secret 資料就會變成明碼,要小心不要將 Secret 透過 log 之類的工具輸出,以免洩漏 Secret。

範例:設定資料庫

本次範例繼續延續前面建立 VPC、Subnet 的範例,首先我們需要使用 CLI 將資料庫的帳號密碼設定寫入 Stack 檔案中。

$ pulumi config set --path database.username admin
$ pulumi config set --path database.password test123 --secret

接著就可以開始撰寫 IaC 程式來建立資料庫了。

在程式中,一樣先使用 TypeScript 的 Interface 定義 Database Config。接著使用 requireSecretObject 讀取 Config。雖然我們 config 中混合了一般的 config 與 secret,但在 pulumi 中都可以透過 getSecret / requireSecret 相關的方法讀取。

接著我們就可以建立 SubnetGroup 與 RDS Instance 了。
值得注意的地方在將 databaseConfig 內的 username 與 password 傳遞至 rds.Instance 的方式,是使用到了 lifting 的技巧,讓我們不需要撰寫 username: databaseConfig.apply(db => db.username) 這樣的 Output 轉換程式,就能取得 username Output 值。

// 建立 DatabaseConfig interface 用來代表資料庫設定的 config 
interface DatabaseConfig {
    username: string;
    password: string;
}

// 使用 requireSecretObject 讀取含有 Secret 的 config
const databaseConfig = config.requireSecretObject<DatabaseConfig>("database");

// 在 AWS 中,建立資料庫前需要先有 SubnetGroup,SubnetGroup 中的 Subnet 至少要在 2 個以上的 availability zone,這樣 RDS instance 設定 multiAz 時,才能有另一個可用區可以使用。
const dbSubnet = new aws.rds.SubnetGroup("my-db-subnet", {
    subnetIds: Object.values(privateSubnets).map(subnet => subnet.id),
});

// 建立一個 mysql 資料庫,使用 config 中的帳號密碼
const db = new aws.rds.Instance("my-db", {
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    engine: "mysql",
    username: databaseConfig.username,
    password: databaseConfig.password,
    dbSubnetGroupName: dbSubnet.name,
    skipFinalSnapshot: true,
});

後記

今天的內容比較短一點,本想與昨天的 Config 合併再一起寫,但是又會造成 config 文章太長。我也擔心後面沒有主題寫。明天的文章會介紹 CustomResource,再來就會進入到一個比較複雜的實戰案例。然後就會進入 Part2,開始寫一些比較進階的議題。

如果讀者對 Pulumi 有任何問題,或是有想了解的內容,也可以在留言許願。畢竟我也還沒想好之後要寫啥。


上一篇
[Day 11] Pulumi 的 Stack Config
下一篇
[Day 13] Pulumi 的兩種 Resource
系列文
30 天學習 Pulumi:用各種程式語言控制雲端資源30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言